package logger

import (
	"fmt"
	"os"
	"runtime/debug"
	"sync"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var (
	logger      *zap.Logger
	once        sync.Once
	atomicLevel zap.AtomicLevel

	defaultProperties = Properties{
		Encoder: ConsoleEncoder,
		Level:   InfoLevel,
	}
)

func BuildLogger(properties Properties) {
	once.Do(func() {
		encoderCfg := zap.NewProductionEncoderConfig()

		var encoder zapcore.Encoder
		switch properties.Encoder {
		case JsonEncoder:
			encoder = zapcore.NewJSONEncoder(encoderCfg)
		case ConsoleEncoder:
			encoder = zapcore.NewConsoleEncoder(encoderCfg)
		}

		atomicLevel = zap.NewAtomicLevel()
		SetLevel(properties.Level)

		logger = zap.New(zapcore.NewCore(encoder, os.Stdout, atomicLevel), zap.AddCaller())

		logger.Sugar().Infof("logger initialized with encoder - '%s', level - '%s'", properties.Encoder, properties.Level)
		logger.Sugar().Debug(string(debug.Stack()))
	})
}

func SetLevel(level Levels) {
	switch level {
	case DebugLevel:
		atomicLevel.SetLevel(zapcore.DebugLevel)
	case InfoLevel:
		atomicLevel.SetLevel(zapcore.InfoLevel)
	default:
		panic(fmt.Errorf("invalid log level '%s' specified for logger", level))
	}
}

func CurrentLevel() string {
	return atomicLevel.String()
}

// Logger returns a global logger defined in this package.
// If logger is nil function returns a logger with DEBUG level.
func Logger() *zap.Logger {
	if logger == nil {
		BuildLogger(defaultProperties)
	}

	return logger
}
